package timing.ukulele.flow.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import timing.ukulele.common.ResponseResult;
import timing.ukulele.common.ResultCode;
import timing.ukulele.common.item.ItemData;
import timing.ukulele.flow.client.ItemClient;
import timing.ukulele.flow.dictionary.FlowStatusEnum;
import timing.ukulele.flow.extention.CustomBpmnJsonConverter;
import timing.ukulele.flow.persistent.Flow;
import timing.ukulele.flow.vo.BpmnModelVO;
import io.swagger.annotations.ApiParam;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Service
public class ModelService {
    private final RepositoryService repositoryService;
    private final FlowService flowService;
    private final ObjectMapper objectMapper;
    @Resource
    private ItemClient itemClient;

    public ModelService(RepositoryService repositoryService, FlowService flowService, ObjectMapper objectMapper) {
        this.repositoryService = repositoryService;
        this.flowService = flowService;
        this.objectMapper = objectMapper;
    }

    public ResponseResult<String> saveBpmn(@RequestBody BpmnModelVO bpmn) {
        if (bpmn == null || !StringUtils.hasLength(bpmn.getBpmnXml()) || StringUtils.isEmpty(bpmn.getTenantId())
                || StringUtils.isEmpty(bpmn.getProcessKey()) || StringUtils.isEmpty(bpmn.getName()))
            return ResponseResult.error("参数不完整");
        Model modelData = null;
        if (StringUtils.hasLength(bpmn.getModelId()))
            modelData = repositoryService.getModel(bpmn.getModelId());
        if (modelData == null) {
            modelData = repositoryService.newModel();
        }
        //与前端保持一致
        modelData.setKey("p_" + bpmn.getProcessKey());
        modelData.setName(bpmn.getName());
        modelData.setTenantId(bpmn.getTenantId());
        try {
            XMLInputFactory xif = XMLInputFactory.newInstance();
            InputStream is = new ByteArrayInputStream(bpmn.getBpmnXml().getBytes(StandardCharsets.UTF_8));
            XMLStreamReader xtr = xif.createXMLStreamReader(is);
            // 转为bpmnModel
            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
            // bpmnModel转json
            CustomBpmnJsonConverter converter = new CustomBpmnJsonConverter();
            ObjectNode jsonNodes = converter.convertToJson(bpmnModel);
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonXml = objectMapper.writeValueAsString(jsonNodes);
            this.saveModel(modelData, jsonXml, bpmn.getProcessKey());
        } catch (XMLStreamException | JsonProcessingException e) {
            e.printStackTrace();
            return ResponseResult.error(e.getMessage());
        }
        return ResponseResult.success(modelData.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveModel(Model modelData, String jsonXml, Long flowId) {
        repositoryService.saveModel(modelData);
        repositoryService.addModelEditorSource(modelData.getId(), jsonXml.getBytes(StandardCharsets.UTF_8));
        //更新流程模型信息
        flowService.lambdaUpdate().set(Flow::getFlowKey, modelData.getId()).set(Flow::getProcessKey, "p_" + flowId)
                .set(Flow::getStatus, FlowStatusEnum.NEW_VERSION).eq(Flow::getId, flowId).update();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseResult<String> removeModel(Long flowId) {
        ResponseResult<List<ItemData>> items = itemClient.getItemByFlowId(flowId);
        if (items == null || items.getCode() == ResultCode.RPC_ERROR.getCode()) {
            return ResponseResult.error("查询绑定该流程是事项出错");
        }
        List<ItemData> list = items.getData();
        if (!CollectionUtils.isEmpty(list)) {
            int count = list.size();
            String msg = String.format("该流程已绑定以下%s个事项(此处只显示5个以内)，不能删除：", count);
            if (count > 5)
                count = 5;
            for (int i = 0; i < count; i++) {
                msg += list.get(i).getName() + "\n";
            }
            return ResponseResult.error(msg);
        }
        Flow flow = flowService.getById(flowId);
        if (!StringUtils.isEmpty(flow.getFlowKey())) {
            Model model = repositoryService.getModel(flow.getFlowKey());
            if (model != null) {
                if (!StringUtils.isEmpty(model.getKey())) {
                    long count = repositoryService.createDeploymentQuery().deploymentKey(model.getKey()).count();
                    if (count > 0) {
                        return ResponseResult.error("流程已经发布，不可删除");
                    }
                }
                repositoryService.deleteModel(flow.getFlowKey());
            }
        }
        flowService.removeById(flowId);
        return ResponseResult.success();
    }

    public ResponseResult<String> getBpmnXmnById(@ApiParam(value = "bpmn模型id", required = true) @PathVariable("modelId") String modelId) {
        Model modelData = repositoryService.getModel(modelId);
        if (modelData != null)
            try {
                byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());
                ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(modelEditorSource);
                CustomBpmnJsonConverter converter = new CustomBpmnJsonConverter();
                BpmnModel model = converter.convertToBpmnModel(modelNode);// 转model
                byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);// 转xml
                return ResponseResult.success(new String(bpmnBytes));
            } catch (IOException e) {
                e.printStackTrace();
                return ResponseResult.error("获取失败");
            }
        return ResponseResult.error("不存在该模型");
    }

    public ResponseResult createFlowModel(@RequestParam("tenantId") String tenantId,
                                          @RequestParam("modelId") String modelId,
                                          @RequestParam("processKey") String processKey,
                                          @RequestParam("category") String category,
                                          @RequestParam("processName") String processName,
                                          @RequestParam("processDescription") String processDescription,
                                          @RequestBody String jsonXml) {
        try {
            Model modelData = repositoryService.getModel(modelId);//先通过modelId去查找Model记录
            //1.如果modelDate没有就创建
            if (modelData == null) {
                modelData = repositoryService.newModel();
            }
            //然后修改属性信息

            // String processName = "流程名称";
            // String processkey = "流程Key";
            // String category = "分类";
            // String documentation = "流程描述";

            // 创建model需要的MetaInfo属性数据
            ObjectNode modelObjectNode = objectMapper.createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processName);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);// 版本
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDescription);
            modelData.setMetaInfo(modelObjectNode.toString());// 设置MetaInfo属性数据
            modelData.setTenantId(tenantId);

            modelData.setName(processName);
            modelData.setKey(processKey);
            modelData.setCategory(category);
            // 保存模型
            repositoryService.saveModel(modelData);

            //2.保存二进制源xml

            repositoryService.addModelEditorSource(modelData.getId(), jsonXml.getBytes("UTF-8"));

            //3.保存流程图片svg
            // InputStream in = null;
            // try {
            //     in = new ByteArrayInputStream(svgXml.getBytes("UTF-8"));
            // } catch (UnsupportedEncodingException e) {
            //     e.printStackTrace();
            // }
            // InputStreamSource xmlSource = new InputStreamSource(in);
            //     BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
            //     BpmnModel bpmnModel = bpmnXMLConverter.convertToBpmnModel(xmlSource, true, false, "UTF-8");
            //     List highLightedActivities = new ArrayList();
            //     MyDefaultProcessDiagramGenerator defaultProcessDiagramGenerator = new MyDefaultProcessDiagramGenerator();
            //     List<String> stringList = new ArrayList<>();
            //     stringList.add("png");
            //     InputStream is = defaultProcessDiagramGenerator.generateDiagram(bpmnModel, stringList, highLightedActivities);
            //     ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //     try {
            //         byte[] buff = new byte[1024];
            //         while ((is.read(buff)) != -1) {
            //             baos.write(buff);
            //         }
            //         byte[] result = baos.toByteArray();
            //         // 添加图片源
            //         this.repositoryService.addModelEditorSourceExtra(modelData.getId(), result);
            //     } catch (IOException e) {
            //         throw new ActivitiException("流程图创建失败！");
            //     }
        } catch (Exception e) {
            return ResponseResult.error("创建模型失败");
        }
        return ResponseResult.success("创建模型成功");
    }
}
